<?php

/* User and group related classes
 *
 * This collection of functions (library) is licensed under GPL2.
 * See LICENSE or www.gnu.org for more details.
 * @author Jyry Kuukkanen jyry@neutech.fi
 *
 * $Id: dzuser.php 597 2006-04-03 16:15:46Z jyry $
 */


include_once("sosqlhi.php");

/* generic dzuser related tables */
define(DZU_TABLE, "dzusers");
define(DZU_GROUPTABLE, "dzusergroups");
define(DZU_LANGTABLE, "dzlangs");

/* User and user group related columns */
define(DZU_IDCOL, DZU_TABLE."."."id");
define(DZU_PASSWORDCOL, DZU_TABLE."."."password");
define(DZU_LEVELCOL, DZU_TABLE."."."level");
define(DZU_INACTIVECOL, DZU_TABLE."."."inactive");
define(DZU_MAXSESSIONSCOL, DZU_TABLE."."."max_sessions");
define(DZU_LANGUAGECOL, DZU_TABLE."."."language");
define(DZU_FIRSTNAMECOL, DZU_TABLE."."."firstname");
define(DZU_SURNAMECOL, DZU_TABLE."."."surname");
define(DZU_TITLECOL, DZU_TABLE."."."title");
define(DZUG_USERCOL, DZU_GROUPTABLE."."."userid");
define(DZUG_GROUPCOL, DZU_GROUPTABLE."."."groupid");
define(DZUL_IDCOL, DZU_GROUPTABLE."."."id");
define(DZUL_NAMECOL, DZU_GROUPTABLE."."."name");
define(DZUL_LOCALCOL, DZU_GROUPTABLE."."."local");

/* Info request types */
define(DZU_PASSWORD, "password");
define(DZU_LEVEL, "level");
define(DZU_INACTIVE, "inactive");
define(DZU_MAXSESSIONS, "max_sessions");
define(DZU_LANG, "language");
define(DZU_FIRSTNAME, "firstname");
define(DZU_SURNAME, "surname");
define(DZU_FULLNAME, "fullname");           /* Firstname Surname */
define(DZU_SURFULLNAME, "surfullname");     /* Surname Firstname */
define(DZU_GROUPS, "groups");
define(DZU_STATUS, "status");

/* Initial admin user */
define(DZU_ADMIN_INIT_ID, "dzadmin");
define(DZU_ADMIN_INIT_FIRSTNAME, "Admin");
define(DZU_ADMIN_INIT_SURNAME, "Dataxi");
define(DZU_ADMIN_INIT_PASSWORD, "nalle");
define(DZU_ADMIN_INIT_LANG, "en");
define(DZU_ADMIN_INIT_LEVEL, 100);

/* Error status codes */
define(DZU_ERR_NONE, 0);
define(DZU_ERR_BADUSER, -1);
define(DZU_ERR_BADTYPE, -2);
define(DZU_ERR_BADQUERY, -3);
define(DZU_ERR_BADGROUP, -4);

/* generic dzgroup related constants and default values */
define(DZG_TABLE, "dzgroups");
define(DZG_IDCOL, DZG_TABLE.".id");
define(DZG_NAMECOL, DZG_TABLE.".name");


/** User class
 *  Query and update tool for Dataxi user table.
 *  @var soDa $__Soda Database access object
 */
class dzUser {

    /* @access private */
    var $__Soda;

    /** constructor
     *  @param string $DbObj Database object
     *  @param soDa $Soda When a soDa object is already available, it may
     *      be passed to the constructor and used by the class instead of
     *      creating (yet) another instance of it. $DbAccessStr may be
     *      passed as an empty string ("") if the DbAccess is already set
     *      in the passed soDa object.
     */
    function dzUser(&$DbObj, $Options = array()) {
        $this->__Soda = new soDa($DbObj, $Options);
    } // dzUser


    /** Returns certain information about the user.
     *  Any DZU_* type of information can be retrieved, see defines in the
     *  begining of the file for details. Some types return a list of values,
     *  like DZU_FULLNAME (surname+firstname) and DZU_GROUPS (a list of all
     *  groups the user belongs to). This list is delimited by $Delim, that
     *  defaults to a space.
     *  Example: getInfo(array(DZU_LEVEL), "name") would return user's "name"
     *      level
     *  Example: getInfo(array(DZU_LANG), "pekka") would return user's
     *      "pekka" language
     *  Example: getInfo(array(DZU_LANG), "pekka") would return an error, if
     *      "pekka" was not found.
     *  Also, $this->Level is set according to user level.
     *  @param array/DZU_* $Types Types of information to get for a user.
     *  @param string $UserID User ID to get info for.
     *  @param string $Delim Return values delimiter.
     *  @param bool $__AutoCreate Flag to indicate whether createTables should
     *  be called when query fails miserably. This is used by internally by
     *  the function itself.
     *  @return array/any Returns associative array of requested information
     *      for a user. A special item DZU_STATUS may be returned in the
     *      result set and it then contains one of the following value:
     *      DZU_ERR_BADUSER No such user or empty user id.
     *      DZU_ERR_BADTYPE Bad type request
     *      DZU_ERR_BADQUERY Sql error; connection to db problem?
     *          Table missing, perhaps?
     */
    function getInfo($Types, $UserId, $Delim = " ", $__AutoCreate = 1) {

        if (gettype($Types) != "array") $Types = array($Types);

        /* user id check */
        if (!$UserId) {
            soDebug("dzUser->getInfo: bad user ID = '$UserId'",
                    SOD_HI);
            return array(DZU_STATUS => DZU_ERR_BADUSER);  // <-- EXIT
        }; // if

        foreach ($Types as $type) {
            /* type check */
            if (!in_array($type, array(DZU_LANG, DZU_PASSWORD, DZU_INACTIVE,
                                       DZU_INACTIVE, DZU_LEVEL, DZU_FIRSTNAME,
                                       DZU_GROUPS, DZU_SURNAME,
                                       DZU_SURFULLNAME, DZU_FULLNAME,
                                       DZU_MAXSESSIONS))) {
                soDebug("dzUser->getInfo: bad type = '$type' User='$UserId'",
                        SOD_HI);
                return array(DZU_STATUS => DZU_ERR_BADTYPE); // <-- EXIT
            }; // if
        }; // foreach

        $this->__Soda->setAction(SODA_SELECT);

        /* Query user table, if required so */
        $uresults = NULL;
        if (!((count($Types) == 1) && ($Types[0] == DZU_GROUPS))) {
            $this->__Soda->setTable(DZU_TABLE, 1);
            $this->__Soda->setSection(SODA_DATA,
                                      DZU_PASSWORDCOL." ".
                                      DZU_FIRSTNAMECOL." ".
                                      DZU_SURNAMECOL." ".
                                      DZU_INACTIVECOL.":int ".
                                      DZU_MAXSESSIONSCOL.":int ".
                                      DZU_LEVELCOL.":int ".
                                      DZU_LANGUAGECOL, array());
            $this->__Soda->setSection(SODA_WHERE, DZU_IDCOL, array($UserId));
            if ($this->__Soda->run()) {
                $uresults = unserialize(serialize($this->__Soda->Results));
            }; // if
        }; // if

        /* Query user group table, if required so */
        $gresults = NULL;
        if (in_array(DZU_GROUPS, $Types)) {
            $this->__Soda->setTable(DZU_GROUPTABLE, 1);
            $this->__Soda->setSection(SODA_DATA,
                                      DZUG_USERCOL." ".
                                      DZUG_GROUPCOL, array());
            $this->__Soda->setSection(SODA_WHERE, DZUG_USERCOL,
                                      array($UserId));
            if ($this->__Soda->run()) {
                $gresults = $this->__Soda->Results;
            }; // if
        }; // if

        $result = array();
        if ($uresults || $gresults) {
            if ($uresults && $uresults->count() != 1) {
                $result = array(DZU_STATUS => DZU_ERR_BADUSER);
            } else {
                foreach ($Types as $type) {
                    switch ($type) {
                        case DZU_FULLNAME:
                            $result[DZU_FULLNAME] =
                                $uresults->getValue(DZU_FIRSTNAMECOL).
                                $Delim.
                                $uresults->getValue(DZU_SURNAMECOL);
                            break;

                        case DZU_SURFULLNAME:
                            $result[DZU_SURFULLNAME] =
                                $uresults->getValue(DZU_SURNAMECOL).
                                $Delim.
                                $uresults->getValue(DZU_FIRSTNAMECOL);
                            break;

                        case DZU_GROUPS:
                            $result[DZU_GROUPS] =
                                implode($Delim,
                                $gresults->getCol(DZUG_GROUPCOL));
                            break;

                        default:
                            $result[$type] =
                                $uresults->getValue(DZU_TABLE.".".$type);
                    }; // switch
                }; // foreach
            }; // else if results->count
        } else {
            /* Query failed? Table missing? Try creating table. */
            if (($__AutoCreate) && ($this->createTables())) {
                $result = $this->getInfo($Types, $UserId, $Delim, 0);
            } else {
                soDebug("dzUser->getInfo: bad query ".
                        "Types='".soDumpArray($Types, "", $Delim, "=").
                        "' User='$UserId'", SOD_HI);
                $result[DZU_STATUS] = DZU_ERR_BADQUERY;
            };
        }; // else if $sql->run()

        return $result;
    } // getInfo


    /** Changes the user password
     *  @param string $UserId User ID
     *  @param string $OldPassword Old user password to change from
     *  @param string $NewPassword New password to change to
     *  @return bool True(1) for good password change, false(0) for not so
     *      good: old password did not match or update failed.
     */
    function changePassword($UserId, $OldPassword, $NewPassword) {
        $user_info = $this->getInfo(array(DZU_PASSWORD), $UserId);
        $password = $user_info[DZU_PASSWORD];
        if ($password == $OldPassword) {
            $this->__Soda->clear();
            $this->__Soda->setTable(DZU_TABLE, 1);
            $this->__Soda->setAction(SODA_UPDATE);
            $this->__Soda->setSection(SODA_DATA, DZU_PASSWORDCOL,
                                                  array($NewPassword));
            $result = $this->__Soda->run();
        } else {
            $result = 0;
        };

        return $result;
    } // changePassword


    /** Creates user tables
     *  @return bool True(1) on success or false(1) on error.
     */
    function createTables() {
        $this->__Soda->clear();
        $this->__Soda->setAction(SODA_CREATE);
        $this->__Soda->setTable(DZU_TABLE, 1);
        $this->__Soda->setSection(SODA_DATA, array(
                                  DZU_IDCOL,
                                  DZU_FIRSTNAMECOL,
                                  DZU_SURNAMECOL,
                                  DZU_TITLECOL,
                                  DZU_PASSWORDCOL,
                                  DZU_LEVELCOL,
                                  DZU_LANGUAGECOL,
                                  DZU_INACTIVE,
                                  DZU_MAXSESSIONS,
                                  "*"),
                                  array(
                                  "char(12) not null",
                                  "char(32)",
                                  "char(32)",
                                  "char(40)",
                                  "char(32)",
                                  "int default 0",
                                  "char(5)",
                                  "int default 0",
                                  "int default 0",
                                  "id"));
        $result = $this->__Soda->run();

        /* Main table created fine? */
        if ($result) {
            /* Main table ok -> insert default admin user to it */
            $this->__Soda->setAction(SODA_INSERT);
            $this->__Soda->setSection(SODA_DATA, array(
                                      DZU_IDCOL,
                                      DZU_FIRSTNAMECOL,
                                      DZU_SURNAMECOL,
                                      DZU_PASSWORDCOL,
                                      DZU_LANG,
                                      DZU_LEVELCOL.":int"),
                                      array(
                                      DZU_ADMIN_INIT_ID,
                                      DZU_ADMIN_INIT_FIRSTNAME,
                                      DZU_ADMIN_INIT_SURNAME,
                                      md5(DZU_ADMIN_INIT_PASSWORD),
                                      DZU_ADMIN_INIT_LANG,
                                      DZU_ADMIN_INIT_LEVEL));
            $result = $this->__Soda->run();
        }; // if

        /* Main table & initial user ok? */
        if ($result) {
            $this->__Soda->setAction(SODA_CREATE);
            $this->__Soda->setTable(DZU_GROUPTABLE, 1);
            $this->__Soda->setSection(SODA_DATA, array(
                                      "userid",
                                      "groupid",
                                      "*"),
                                      array(
                                      "char(12) not null",
                                      "char(12) not null",
                                      "userid,groupid"));
            $result = $this->__Soda->run();
        }; // if

        if ($result) {
            $dzgroup = new dzUserGroup($this->__Soda);
            $result = $dzgroup->createTable();
            $this->__createLangTable();
        }; // if

        return ($result);
    } // createTables


    /** Creates the language table
     *  Called by createTables
     */
    function __createLangTable() {
        $this->__Soda->setAction(SODA_CREATE);
        $this->__Soda->setTable(DZU_LANGTABLE, 1);
        $this->__Soda->setSection(SODA_DATA, array(
                                  "id",
                                  "name",
                                  "local",
                                  "*"),
                                  array(
                                  "char(5) not null",
                                  "char(30) not null",
                                  "char(30)",
                                  "id"));
        $this->__Soda->run();

        $this->__Soda->setAction(SODA_INSERT);
        $this->__Soda->setSection(SODA_DATA,
                                  array(DZUL_IDCOL,
                                        DZUL_NAMECOL,
                                        DZUL_LOCALCOL),
                                  array("en", "English", "English"));
        $this->__Soda->run();
    } // __createLangTable


}; // dzUser


/** Group class
 *  Query and update tool for Dataxi group table.
 *  @var soDa $__Soda Database access object
 */
class dzUserGroup {
    /* @access private */
    var $__Soda;


    /** Constructor
     *  @param object $DbObj Database object (PEAR)
     *  @param array/any Database options (trimming etc.)
     */
    function dzUserGroup(&$Soda) {
        $this->__Soda = $Soda;
        $this->__Soda->setTable(DZG_TABLE, 1);
    } // dzUserGroup


    /** Returns certain information about the group.
     *  Any DZG_* type of information can be retrieved, see defines in the
     *  begining of the file for details.
     *  Example: getInfo(DZG_NAME, "group") would return user's "group" name
     *  @param DZG_* $Type Type of information to get for a group.
     *  @param string $GroupID Group ID to get info for.
     *  @return any Returns requested information type for a group or error no:
     *      DZG_ERR_BADGROUP No such group or empty group id.
     *      DZG_ERR_BADTYPE Bad type request
     *      DZG_ERR_BADQUERY Sql error; connection to db problem?
     *          Table missing, perhaps?
     */
    function getInfo($Type, $GroupId) {

        /* user id check */
        if (!$GroupId) {
            soDebug("dzUserGroup->getInfo: bad group ID Type=$Type ".
                    "Group=$GroupId", SOD_HI);
            return DZG_ERR_BADGROUP;  // <-- EXIT
        }; // if

        /* type check */
        if ($Type != DZG_NAME) {
            soDebug("dzUserGroup->getInfo: bad type Type='$Type' ".
                    "Group='$GroupId'", SOD_HI);
            return DZG_ERR_BAD; // <-- EXIT
        };

        $this->__Soda->setAction(SODA_SELECT, 1);
        $this->__Soda->setSection(SODA_DATA, DZG_IDCOL." ".DZG_NAMECOL,
                                  array());
        $this->__Soda->setSection(SODA_WHERE, DZG_IDCOL, array($GroupId));
        if ($this->__Soda->run()) {
            if ($this->__Soda->Results->count() < 1) {
                $result = DZG_ERR_BADUSER;
            } else {
                $result = $this->__Soda->getDataVal(DZG_TABLE.".".$Type);
            }; // else if results->count

        } else {
            soDebug("dzUserGroup->getInfo: bad query Type='$Type' ".
                    "Group='$GroupId'", SOD_HI);
            $result = DZG_ERR_BADQUERY;
        }; // else if $sql->run()

        soDebug("dzUserGroup->getInfo: Type=$Type Group=$GroupId", SOD_DET);
        return $result;
    } // getInfo


    /** Creates group table
     *  @return bool True(1) on success or false(1) on error.
     */
    function createTable() {
        $this->__Soda->setAction(SODA_CREATE);
        $this->__Soda->setSection(SODA_DATA, DZG_IDCOL." ".DZG_NAMECOL,
                                  array("char(12) not null", "char(32)"));
        $result = $this->__Soda->run();

        if ($result) {
            /* If create succeeded, then insert default admin group to it */
            $this->__Soda->setAction(SODA_INSERT);
            $this->__Soda->setSection(SODA_DATA, DZG_IDCOL." ".DZG_NAMECOL,
                                      array(strtoupper(DZU_ADMIN_INIT_ID),
                                            DZU_ADMIN_INIT_FIRSTNAME));
            $result = $this->__Soda->run();
        };

        return (!$result);
    } // createTable

}; // dzUserGroup

?>
